package com.stars.aspect;

import com.stars.common.MyDateUtils;
import com.stars.common.annotation.Log;
import com.stars.common.annotation.UserMenuUpdateRedis;
import com.stars.common.base.BaseConstant;
import com.stars.common.base.BaseMsg;
import com.stars.common.base.ResultMsg;
import com.stars.common.cons.RedisCons;
import com.stars.common.date.DateUtil;
import com.stars.common.enums.OperationType;
import com.stars.common.maps.JsonUtils;
import com.stars.entity.dto.sys.SystemUserAppOrgDTO;
import com.stars.entity.system.OperationLogEntity;
import com.stars.services.system.OperationLogConfigServices;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * @author csot.qhluodajie on 2019/1/2.
 */
@Aspect
@Order(1)
@Component
public class WebLogAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    ThreadLocal<Long> startTime = new ThreadLocal<>();
    //异常类
    private static final String EXCEPTION_CLASS ="com.stars.controller.advice.CommonExceptionHandler.commonException";
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private OperationLogConfigServices operationLogConfigServicesImpl;

    /**
     * 获取用户信息
     *
     * @param request
     * @return
     * @throws Exception
     */
    private SystemUserAppOrgDTO getUserInfo(HttpServletRequest request) {
        String accessToken = request.getHeader(BaseConstant.ACCESS_TOKEN);
        if (StringUtils.isBlank(accessToken)) {
            return null;
        }
        String redisUserId = redisTemplate.opsForValue().get(accessToken);
        SystemUserAppOrgDTO systemUser = JsonUtils.jsonToPojo(redisTemplate.opsForValue().get(redisUserId + BaseConstant.ACCESS_TOKEN), SystemUserAppOrgDTO.class);
        return systemUser;
    }

    //切面包
    @Pointcut("execution(public * com.stars.controller..*.*(..))")
    public void web() {
    }

    //注解切面
    @Pointcut("@annotation(log))")
    public void webLog(Log log) {
    }

    @Pointcut("@annotation(userMenuUpdateRedis)")
    private void menuUpdate(UserMenuUpdateRedis userMenuUpdateRedis) {

    }

    @Before("web()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());

        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }
    @After("web()")
    public void releaseResource(JoinPoint point) throws Exception {

        String targetName =  point.getSignature().getDeclaringTypeName() +"." + point.getSignature().getName();
       //异常添加日志
        if(EXCEPTION_CLASS.equals(targetName)){
            // 接收到请求，记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            long time = System.currentTimeMillis();
            time = System.currentTimeMillis() - time;
            SystemUserAppOrgDTO user = getUserInfo(request);
            OperationLogEntity log = new OperationLogEntity();
            log.setRunTime(time);
            log.setUrl(request.getRequestURL().toString());
            log.setSysIp(request.getRemoteAddr());
            log.setMethod(point.getTarget().toString());
            log.setCreateId(user.getId());
            log.setCreateName(user.getUserRealName());
            log.setAppId(user.getAppId());
            log.setAppName(user.getAppName());
            log.setCreateDate(MyDateUtils.getNowDate(MyDateUtils.YYYY_MM_DD_HH_MM_SS));
            log.setOperationUnit(point.getSignature().getDeclaringTypeName());
            log.setReturnValue(Arrays.toString(point.getArgs()));
            log.setLevel(2);
            log.setOperationType(OperationType.EXCEPTION.getValue());
            log.setDescribeLog(BaseMsg.SYSTEM_ERROR);

            operationLogConfigServicesImpl.insertSelective(log);

        }
    }

    @AfterReturning(returning = "ret", pointcut = "web()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求，返回内容
        //logger.info("RESPONSE : " + ret);
        logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
    }

    /**
     * 环绕增强，相当于MethodInterceptor
     */
    @Around("webLog(log)")
    public Object doAround(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
        Object res = null;
        long time = System.currentTimeMillis();
        res = joinPoint.proceed();
        time = System.currentTimeMillis() - time;
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //添加日志
        addOperationLog(joinPoint, res, time, request, log);

        return res;

    }

    /**
     * @Description: 监听菜单更新
     * @Param: [joinPoint, userMenuUpdateRedis]
     * @return: java.lang.Object
     * @Author: dj
     * @Date: 2019/10/25
     */
    @Around("@annotation(userMenuUpdateRedis)")
    public Object doMenuUpdate(ProceedingJoinPoint joinPoint, UserMenuUpdateRedis userMenuUpdateRedis) throws Throwable {
        Object res = joinPoint.proceed();
        if (null == res) {
            return null;
        }
        // 接收到请求，记录请求内容
        if (userMenuUpdateRedis != null && userMenuUpdateRedis.permission()) {
            String jsonStr = JsonUtils.objectToJson(res);
            ResultMsg resultMsg = JsonUtils.jsonToPojo(jsonStr, ResultMsg.class);
            if (null != resultMsg && resultMsg.isSuccess()) {
                redisTemplate.opsForValue().set(RedisCons.MENU_LAST_UPDATE, DateUtil.getNewTime() + "");
            }
        }
        return res;
    }

    /**
     * 添加操作日志
     *
     * @param joinPoint
     * @param res
     */
    private void addOperationLog(JoinPoint joinPoint, Object res, long runTime, HttpServletRequest request, Log annotation) throws Exception {

        SystemUserAppOrgDTO user = getUserInfo(request);
        if (null == user) {
            return;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        if (annotation != null) {
            //不记录
            if (OperationType.SELECT.getValue().equals(annotation.operationType().getValue())) {
                return;
            }
            OperationLogEntity operationLog = new OperationLogEntity();

            operationLog.setRunTime(runTime);
            operationLog.setUrl(request.getRequestURL().toString());
            operationLog.setSysIp(request.getRemoteAddr());
            operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());
            operationLog.setCreateId(user.getId());
            operationLog.setCreateName(user.getUserRealName());
            operationLog.setAppId(user.getAppId());
            operationLog.setAppName(user.getAppName());
            operationLog.setCreateDate(MyDateUtils.getNowDate(MyDateUtils.YYYY_MM_DD_HH_MM_SS));
            operationLog.setOperationUnit(joinPoint.getSignature().getDeclaringTypeName());
            if (null != res) {
                operationLog.setReturnValue(JsonUtils.objectToJson(res));
                operationLog.setArgs(Arrays.toString(joinPoint.getArgs()));
            }
            operationLog.setLevel(annotation.level());
            operationLog.setDescribeLog(annotation.detail());
            operationLog.setOperationType(annotation.operationType().getValue());

            operationLogConfigServicesImpl.insertSelective(operationLog);
        }
    }

}
